home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright (C) 2004 Nigel Horne <njh@bandsman.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
- */
- #include "stdafx.h"
-
- #include "resource.h"
- #include "clamav.h"
-
- #include "mainfrm.h"
-
- #include "clamserver.h"
- #include "servername.h"
-
- #include <io.h>
- #include <winsock.h>
- #include <sys/stat.h>
-
- ClamServer::ClamServer(void)
- {
- if(!InitInstance())
- THROW(new CException()); // FIXME: never freed
-
- progressBar = NULL;
- stopping = FALSE;
- }
-
- ClamServer::ClamServer(CString& serverName, unsigned short p)
- {
- LPTSTR hostname = serverName.GetBuffer(64);
- serverIP = inet_addr(hostname);
-
- if(serverIP == -1L)
- THROW(CException());
-
- port = p;
-
- const int sock = CreateConnection();
-
- if(sock < 0)
- THROW(CException());
-
- progressBar = NULL;
- stopping = FALSE;
- }
-
- ClamServer::~ClamServer()
- {
- if(progressBar) {
- delete progressBar;
- progressBar = NULL;
- }
- }
-
- BOOL
- ClamServer::InitInstance(void)
- {
- ServerName serverName;
-
- if(serverName.DoModal() == IDCANCEL)
- return FALSE;
-
- const char *hostname = serverName.m_serverName;
-
- serverIP = inet_addr(hostname);
-
- if(serverIP == -1L) {
- AfxMessageBox("Unknown host");
- return FALSE;
- }
-
- port = (unsigned short)serverName.m_port;
- const int sock = CreateConnection();
-
- if(sock < 0)
- return TRUE;
-
- return CheckConnection(sock);
- }
-
- const BOOL
- ClamServer::CheckConnection(int sock)
- {
- if(send(sock, "PING\n", 5, 0) < 5) {
- closesocket(sock);
- AfxMessageBox("Can't talk to clamdserver");
- return FALSE;
- }
- char ret[5];
- if(recv(sock, ret, sizeof(ret), 0) <= 4) {
- closesocket(sock);
- AfxMessageBox("Can't receive from clamdserver");
- return FALSE;
- }
- closesocket(sock);
- if(strncmp(ret, "PONG\n", 5) != 0) {
- AfxMessageBox("Is that server running clamd?");
- return FALSE;
- }
-
- return TRUE;
- }
-
- int
- ClamServer::CreateConnection(void)
- {
- const int sock = socket(AF_INET, SOCK_STREAM, 0);
- if(sock < 0) {
- AfxMessageBox("Can't create socket");
- return FALSE;
- }
-
- struct sockaddr_in server;
- memset(&server, '\0', sizeof(struct sockaddr_in));
- server.sin_family = PF_INET;
- server.sin_port = htons(port);
- server.sin_addr.s_addr = serverIP;
-
- // TODO display a message about connecting to the server. Include cancel button
- if(connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr)) < 0) {
- AfxMessageBox("Can't connect to clamdserver");
- return FALSE;
- }
- return sock;
- }
-
- // TODO: on recursive pop up box with progress bar - to include cancel button
- BOOL
- ClamServer::Scan(const CString& filename, int level, CMainFrame *mainFrame, CWnd *parent, BOOL recursive, const CString& qDir)
- {
- if(level == 0)
- stopping = FALSE;
- else if(stopping) {
- if(progressBar) {
- delete progressBar;
- progressBar = NULL;
- }
- // mainFrame->ChangeStatusText("");
- return TRUE;
- }
-
- // Don't scan folders "." and ".."
- if(filename[filename.GetLength() - 1] == '.')
- return TRUE;
-
- // I understand newer MFCs have 'PathIsDirectory'
-
- struct stat statb;
-
- if(stat(filename, &statb) < 0) {
- // It could be that we've been given a wild card match
-
- WIN32_FIND_DATA findData;
-
- HANDLE hFind = FindFirstFile(filename, &findData);
-
- if(hFind == INVALID_HANDLE_VALUE) {
- // No we haven't...
- AfxMessageBox(CString("Can't stat ") + filename);
- return TRUE;
- }
- return this->ScanWildCard(filename, level, mainFrame, parent, recursive, qDir);
- }
-
- if(progressBar && !stopping) {
- if(progressBar->IsStopPressed())
- stopping = TRUE;
- progressBar->SetFilename(filename);
- }
-
- // mainFrame->ChangeStatusText(filename); // statusBar.ShowProgress
- // mainFrame->UpdateWindow();
-
- if(statb.st_mode&S_IFDIR) {
- // Don't recurse unless we've been asked to
-
- if((!recursive) && (level > 0))
- return TRUE;
-
- if(progressBar == NULL) {
- // FIXME: not all return paths remove this, possible memory leak
- progressBar = new CProgress(parent);
- progressBar->Create(IDD_PROGRESS, parent);
- }
-
- // Have been passed a folder.
- return this->ScanFolder(filename, level, mainFrame, parent, recursive, qDir);
-
- }
-
- if(progressBar && (level == 0)) {
- delete progressBar;
- progressBar = NULL;
- }
-
- const int commandSocket = CreateConnection();
-
- if(commandSocket < 0)
- return TRUE;
-
- if(send(commandSocket, "STREAM\n", 7, 0) < 7) {
- closesocket(commandSocket);
- AfxMessageBox("Send failed to clamd");
- return TRUE;
- }
-
- char buf[64];
- int nbytes = ClamdRecv(commandSocket, buf, sizeof(buf) - 1);
-
- if(nbytes < 0) {
- closesocket(commandSocket);
- AfxMessageBox("recv failed from clamd getting PORT");
- return TRUE;
- }
- buf[nbytes] = '\0';
-
- unsigned short port;
-
- if(sscanf(buf, "PORT %hu\n", &port) != 1) {
- closesocket(commandSocket);
- AfxMessageBox("Didn't get PORT information from clamd");
-
- return TRUE;
- }
-
- const int dataSocket = socket(AF_INET, SOCK_STREAM, 0);
-
- if(dataSocket < 0) {
- closesocket(commandSocket);
- AfxMessageBox("Can't create dataSocket");
- return TRUE;
- }
-
- shutdown(dataSocket, 0);
-
- struct sockaddr_in reply;
- memset(&reply, '\0', sizeof(struct sockaddr_in));
- reply.sin_family = PF_INET;
- reply.sin_port = htons(port);
- reply.sin_addr.s_addr = serverIP;
-
- const int rc = connect(dataSocket, (struct sockaddr *)&reply, sizeof(struct sockaddr_in));
- if(rc < 0) {
- closesocket(commandSocket);
- closesocket(dataSocket);
- AfxMessageBox("Failed to connect to port given by clamd");
- return TRUE;
- }
-
- CFile file;
-
- if(!file.Open(filename, CFile::modeRead|CFile::typeBinary|CFile::shareDenyNone)) {
- closesocket(commandSocket);
- closesocket(dataSocket);
-
- AfxMessageBox(CString("Can't open ") + filename + " to scan: ");
- return TRUE;
- }
-
- if(progressBar)
- progressBar->SetPercent(0);
-
- char buffer[1500]; // TODO: send in MTU byte chunks
- off_t bytesSent = (off_t)0;
-
- BOOL error = FALSE;
-
- while(((nbytes = file.Read(buffer, sizeof(buffer))) > 0) && !stopping) {
- // Every block see if someone wants to do something
- MSG Msg;
-
- if(::PeekMessage(&Msg, NULL, WM_NULL, WM_USER - 1, PM_NOREMOVE)) {
- ::PeekMessage(&Msg, NULL, WM_NULL, WM_USER - 1, PM_REMOVE);
- TranslateMessage(&Msg);
- DispatchMessage(&Msg);
-
- if((progressBar && progressBar->IsStopPressed()) ||
- (Msg.message == WM_QUIT)) {
- error = TRUE;
- break;
- }
- }
-
- char buf[81];
- if(ClamdRecv(commandSocket, buf, sizeof(buf) - 1, 0) > 0) {
- AfxMessageBox(buf);
- error = TRUE;
- break;
- }
-
- if(send(dataSocket, buffer, nbytes, 0) != nbytes) {
- AfxMessageBox("Send error to clamd");
- error = TRUE;
- break;
- }
-
- if(progressBar) {
- bytesSent += nbytes;
-
- progressBar->SetPercent((int)(bytesSent * 100 / statb.st_size));
- }
- }
-
- closesocket(dataSocket);
-
- file.Close();
-
- if(error) {
- closesocket(commandSocket);
- stopping = TRUE;
- if(progressBar && (level == 0)) {
- delete progressBar;
- progressBar = NULL;
- }
- return TRUE;
- }
-
- nbytes = ClamdRecv(commandSocket, buffer, sizeof(buffer) - 1);
-
- closesocket(commandSocket);
-
- if(nbytes < 0) {
- AfxMessageBox("recv error getting status");
- return TRUE;
- } else if(nbytes == 0)
- return TRUE;
-
- buffer[nbytes] = '\0';
-
- if(strstr(buffer, "ERROR") != NULL) {
- AfxMessageBox(filename + " " + buffer);
- return TRUE;
- }
-
- // TODO: if we're scanning down a directory tree
- // don't display a popup box - update a dialog box
- // which tells us how far we are
-
- if(strstr(buffer, "FOUND") == NULL)
- return TRUE;
- AfxMessageBox(filename + " " + buffer);
-
- mainFrame->ChangeStatusText(filename + " " + buffer); // statusBar.ShowProgress
-
- return FALSE;
- }
-
- BOOL
- ClamServer::ScanFolder(const CString& string, int level, CMainFrame *mainFrame, CWnd *parent, BOOL recursive, const CString& qDir)
- {
- return ScanWildCard(string + "\\*.*", level, mainFrame, parent, recursive, qDir);
- }
-
- BOOL
- ClamServer::ScanWildCard(const CString& string, int level, CMainFrame *mainFrame, CWnd *parent, BOOL recursive, const CString& qDir)
- {
- if(stopping)
- return TRUE;
-
- WIN32_FIND_DATA findData;
-
- HANDLE hFind = FindFirstFile(string, &findData);
-
- if(hFind == INVALID_HANDLE_VALUE)
- // No files in this folder
- return TRUE;
-
- // Get to the filename stub - i.e. the file without the trailing \*.*
- const int index = string.Find("\\*.*");
-
- ASSERT(index >= 0);
-
- const CString stub = string.Left(index);
-
- BOOL rc = TRUE;
-
- do
- //if(findData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
- // Recurse into this folder/file only if recurse enabled
- // if(!this->Scan(filename + "\\" + findData.cFileName))
- // break out as soon as one virus is found
- // TODO: optionally report all found
- // return FALSE;
- if(!this->Scan(stub + "\\" + findData.cFileName, level + 1, mainFrame, parent, recursive, qDir))
- rc = FALSE;
-
- while(FindNextFile(hFind, &findData) && !stopping);
-
- if(progressBar && (level == 0)) {
- delete progressBar;
- progressBar = NULL;
- }
-
- return rc;
- }
-
- /*
- * Read from clamav - timeout if necessary
- * timeout defaults to 30 seconds, -1 = wait forever, 0 = poll
- * TODO: default time should be read from clamav.conf
- */
- int
- ClamServer::ClamdRecv(int sock, char *buf, size_t len, int timeout /* = 30 */)
- {
- fd_set rfds;
- struct timeval tv;
-
- if(timeout == -1)
- return recv(sock, buf, len, 0);
-
- FD_ZERO(&rfds);
- FD_SET(sock, &rfds);
-
- tv.tv_sec = timeout; // TODO: from clamav.conf
- tv.tv_usec = 0;
-
- switch(select(sock + 1, &rfds, NULL, NULL, &tv)) {
- case -1:
- AfxMessageBox("select failed");
- return -1;
- case 0:
- if(timeout != 0)
- AfxMessageBox("Timeout waiting for data from clamd");
- return 0;
- }
-
- return recv(sock, buf, len, 0);
- }
-
- // void __cdecl __interrupt __far intFhandler(void) {
- // }
-